package email

import (
	"bytes"
	"encoding/base64"
	"io/ioutil"
	"log"
	"net/smtp"
	"strings"
	"time"
)

// Mail define email interface, and implemented auth and send method
type Mail interface {
	Auth()
	Send(message Message, fileName string) error
}

type SendMail struct {
	User     string
	Password string
	Host     string
	Port     string
	auth     smtp.Auth
}

type Attachment struct {
	Name        string
	ContentType string
	WithFile    bool
}

type Message struct {
	From        string
	To          []string
	Cc          []string
	Bcc         []string
	Subject     string
	Body        string
	ContentType string
	Attachment  Attachment
}

func main() {
	//var mail Mail
	//mail = &SendMail{User: "lyw_programer@163.com", Password: "UCANTFPSLCFNRPEK", Host: "smtp.163.com", Port: "25"}
	//message := Message{From: "lyw_programer@163.com",
	//	To:          []string{"378305181@qq.com"},
	//	Cc:          []string{},
	//	Bcc:         []string{},
	//	Subject:     "HELLO WORLD",
	//	Body:        "testbpdy",
	//	ContentType: "text/plain;charset=utf-8",
	//	Attachment: Attachment{
	//		Name:        "C:/Users/Administrator/Pictures/Camera Roll/4c552f33-2cd8-4c61-a578-eae1ee895dca.png",
	//		ContentType: "image/png",
	//		WithFile:    true,
	//	},
	//}
	//mail.Send(message)
}

func (mail *SendMail) Auth() {
	mail.auth = smtp.PlainAuth("", mail.User, mail.Password, mail.Host)
}

func (mail *SendMail) Send(message Message, fileName string) error {
	mail.Auth()
	buffer := bytes.NewBuffer(nil)
	boundary := "GoBoundary"
	Header := make(map[string]string)
	Header["From"] = message.From
	Header["To"] = strings.Join(message.To, ";")
	Header["Cc"] = strings.Join(message.Cc, ";")
	Header["Bcc"] = strings.Join(message.Bcc, ";")
	Header["Subject"] = message.Subject
	Header["Content-Type"] = "multipart/mixed;boundary=" + boundary
	Header["Mime-Version"] = "1.0"
	Header["Date"] = time.Now().String()
	mail.writeHeader(buffer, Header)

	body := "\r\n--" + boundary + "\r\n"
	body += "Content-Type:" + message.ContentType + "\r\n"
	body += "\r\n" + message.Body + "\r\n"
	buffer.WriteString(body)

	if message.Attachment.WithFile {
		attachment := "\r\n--" + boundary + "\r\n"
		attachment += "Content-Transfer-Encoding:base64\r\n"
		attachment += "Content-Disposition:attachment\r\n"
		attachment += "Content-Type:" + message.Attachment.ContentType + ";name=\"" + fileName + "\"\r\n"
		buffer.WriteString(attachment)
		defer func() {
			if err := recover(); err != nil {
				log.Fatalln(err)
			}
		}()
		mail.writeFile(buffer, message.Attachment.Name)
	}

	buffer.WriteString("\r\n--" + boundary + "--")
	err := smtp.SendMail(mail.Host+":"+mail.Port, mail.auth, mail.User, message.To, buffer.Bytes())
	if err != nil {
		return err
	}
	return nil
}

func (mail *SendMail) writeHeader(buffer *bytes.Buffer, Header map[string]string) string {
	header := ""
	for key, value := range Header {
		header += key + ":" + value + "\r\n"
	}
	header += "\r\n"
	buffer.WriteString(header)
	return header
}

// read and write the file to buffer
func (mail *SendMail) writeFile(buffer *bytes.Buffer, fileName string) {
	file, err := ioutil.ReadFile(fileName)
	if err != nil {
		panic(err.Error())
	}
	payload := make([]byte, base64.StdEncoding.EncodedLen(len(file)))
	base64.StdEncoding.Encode(payload, file)
	buffer.WriteString("\r\n")
	for index, line := 0, len(payload); index < line; index++ {
		buffer.WriteByte(payload[index])
		if (index+1)%76 == 0 {
			buffer.WriteString("\r\n")
		}
	}
}
